"""
All skills are defined as classes in this file. Every skill must have
a canUse() method that takes the gameState, the unit casting, and the coordinates being targeted.
It must also then have a use() method with the same parameters.
"""
import Utility, random, copy
FAIL = 0
ATTACK = 1
PASSIVE = 2
DELAY = 3
HEAL = 4
AOE = 5

#Domingo's Ability
class Freeze:
    name = "Freeze 2"
    description = "Attack 1 space in every direction from target. 50% chance of 2 damage to each target. Range:3"
    def canUse(self, gameState, unitCaster, targetPoint):
        x,y = targetPoint
        
        x1 = unitCaster[1].left // 50
        y1 = unitCaster[1].top // 50
        x2 = x // 50 
        y2 = y // 50
        
        distance = Utility.manhattan((x1,y1), (x2,y2))
        if distance > 3:
            return False
        
        self.victims = []
        targets = [targetPoint, (x+50,y),(x-50,y),(x,y+50),(x,y-50)]
        for p in gameState["players"]:
                for u in p.units:
                    for t in targets:
                        if u[1].collidepoint(t) and unitCaster not in p.units:
                            self.victims.append(u)
        
        return len(self.victims) > 0
        
    def use(self, gameState, unitCaster, targetPoint):
        hits = []
        for v in self.victims:
            if random.randint(0,1) == 1:
                v[0].hp = v[0].hp -2
                hits.append((v,2))
            else:
                hits.append((v,0))
                
        self.targetUnit = None
        self.victims = None    
        return [AOE, hits]
    
    def getOutline(self,x,y):
        outline = [(x-75,y-25), (x-25,y-25), (x-25,y-75), (x+25,y-75), (x+25,y-25), (x+75,y-25), (x+75, y+25), (x+25,y+25), (x+25,y+75), (x-25,y+75), (x-25,y+75), (x-25,y+25), (x-75,y+25)]        
        return outline
#Jessica's ability
class HealLitany:
    name = "Heal Litany"
    description = "Return 1-3 health to an adjacent unit."
    def canUse(self, gameState, unitCaster, targetPoint):
        unitFound = False
        for p in gameState["players"]:
            for u in p.units:
                if u[1].collidepoint(targetPoint):
                    self.targetUnit = u
                    unitFound = True
                    break
                
        if not unitFound:
            return False    
                
        x1 = unitCaster[1].left // 50
        y1 = unitCaster[1].top // 50
        x2 = self.targetUnit[1].left // 50 
        y2 = self.targetUnit[1].top // 50
        
        distance = Utility.manhattan((x1,y1), (x2,y2))
        
        return distance == 1
        
    def use(self, gameState, unitCaster, targetPoint):
        heal = random.randint(1,10)
        if heal <= 3:
            heal = 1
        elif heal <= 8:
            heal = 2
        else:
            heal = 3
        self.targetUnit[0].hp = min(self.targetUnit[0].hp + heal, self.targetUnit[0].maxhp)
        locTarget = self.targetUnit
        self.targetUnit = None
        return [HEAL, locTarget, heal]

#Megaman's ability
class Megabuster:
    name = "Megabuster"
    description = "Charge 1 turn to deal 3x damage."
    delaySet = False
    def canUse(self, gameState, unitCaster, targetPoint):
        unitFound = False
        for p in gameState["players"]:
            for u in p.units:
                if u[1].collidepoint(targetPoint) and not unitCaster in p.units:
                    self.targetUnit = u
                    unitFound = True
                    break
                
        if not unitFound:
            return False    
                
        x1 = unitCaster[1].left // 50
        y1 = unitCaster[1].top // 50
        x2 = self.targetUnit[1].left // 50 
        y2 = self.targetUnit[1].top // 50
        
        distance = Utility.manhattan((x1,y1), (x2,y2))
        
        sight = Utility.lineOfSight((unitCaster[1].centerx, unitCaster[1].centery),
                                    (self.targetUnit[1].centerx, self.targetUnit[1].centery),
                                    gameState)
        
        return sight and distance <= unitCaster[0].range
        
    def use(self, gameState, unitCaster, targetPoint):
        
        if not self.delaySet:
            self.delaySet = True
            return DELAY, 0
        
        else:
            self.delaySet = False
            
            x1 = unitCaster[1].left // 50
            y1 = unitCaster[1].top // 50
            x2 = self.targetUnit[1].left // 50 
            y2 = self.targetUnit[1].top // 50
        
            distance = Utility.manhattan((x1,y1), (x2,y2))
            sight = Utility.lineOfSight((unitCaster[1].centerx, unitCaster[1].centery),
                                    (self.targetUnit[1].centerx, self.targetUnit[1].centery),
                                    gameState)
            
            if distance > unitCaster[0].range or not sight:
                return [FAIL]
            
            hits = defends = 0
            for i in range(unitCaster[0].attack * 3):
                if random.randint(0,1) == 0:
                    hits = hits + 1
            for i in range(self.targetUnit[0].defense):
                if random.randint(0,2) == 0:
                    defends = defends + 1
                
            self.targetUnit[0].hp = self.targetUnit[0].hp - max(0, hits-defends)
            locTarget = self.targetUnit
            self.targetUnit = None
            return ATTACK, locTarget, hits, defends


#Bass' ability
class RapidFire:
    name = "Rapid Fire"
    description = "Make 6 attacks of 1 strength"
    def canUse(self, gameState, unitCaster, targetPoint):
        unitFound = False
        unit = None
        for p in gameState["players"]:
            for u in p.units:
                if u[1].collidepoint(targetPoint) and not unitCaster in p.units:
                    self.targetUnit = u
                    unitFound = True
                    break
                
        if not unitFound:
            return False    
                
        x1 = unitCaster[1].left // 50
        y1 = unitCaster[1].top // 50
        x2 = self.targetUnit[1].left // 50 
        y2 = self.targetUnit[1].top // 50
        
        distance = Utility.manhattan((x1,y1), (x2,y2))
        
        sight = Utility.lineOfSight((unitCaster[1].centerx, unitCaster[1].centery),
                                    (self.targetUnit[1].centerx, self.targetUnit[1].centery),
                                    gameState)
        
        return sight and distance <= unitCaster[0].range
        
    def use(self, gameState, unitCaster, targetPoint):
        
        hits = defends = 0
        for shot in range(6):
            hit = 0
            defend = 0
            if random.randint(0,1) == 0:
                hit = 1
            for i in range(self.targetUnit[0].defense):
                if random.randint(0,2) == 0:
                    defend = defend + 1
            if hit > defend:
                hits = hits + 1
            
        self.targetUnit[0].hp = self.targetUnit[0].hp - max(0, hits-defends)
        locTarget = self.targetUnit
        self.targetUnit = None
        return ATTACK, locTarget, hits, 0
    
#Ghost's ability
class RailGun:
    name = "Rail Gun"
    description = "Ignore line of sight when attacking"
    def canUse(self, gameState, unitCaster, targetPoint):
        unitFound = False
        unit = None
        for p in gameState["players"]:
            for u in p.units:
                if u[1].collidepoint(targetPoint) and not unitCaster in p.units:
                    self.targetUnit = u
                    unitFound = True
                    break
                
        if not unitFound:
            return False    
                
        x1 = unitCaster[1].left // 50
        y1 = unitCaster[1].top // 50
        x2 = self.targetUnit[1].left // 50 
        y2 = self.targetUnit[1].top // 50
        
        distance = Utility.manhattan((x1,y1), (x2,y2))
        
        return distance <= unitCaster[0].range
        
    def use(self, gameState, unitCaster, targetPoint):
        
        hits = defends = 0
        for i in range(unitCaster[0].attack):
            if random.randint(0,1) == 0:
                hits = hits + 1
        for i in range(self.targetUnit[0].defense):
            if random.randint(0,2) == 0:
                defends = defends + 1
                
        self.targetUnit[0].hp = self.targetUnit[0].hp - max(0, hits-defends)
        self.targetUnit = None
        return ATTACK, self.targetUnit, hits, defends

#Raziel's ability
class SoulReaver:
    name = "Soul Reaver"
    description = "Add damage done to own health."
    def canUse(self, gameState, unitCaster, targetPoint):
        unitFound = False
        unit = None
        for p in gameState["players"]:
            for u in p.units:
                if u[1].collidepoint(targetPoint) and not unitCaster in p.units:
                    self.targetUnit = u
                    unitFound = True
                    break
                
        if not unitFound:
            return False

        x1 = unitCaster[1].left // 50
        y1 = unitCaster[1].top // 50
        x2 = self.targetUnit[1].left // 50 
        y2 = self.targetUnit[1].top // 50
        
        distance = Utility.manhattan((x1,y1), (x2,y2))
        
        return distance == 1
    def use(self, gameState, unitCaster, targetPoint):
        hits = defends = 0
        for i in range(unitCaster[0].attack):
            if random.randint(0,1) == 0:
                hits = hits + 1
        for i in range(self.targetUnit[0].defense):
            if random.randint(0,2) == 0:
                defends = defends + 1
                
        self.targetUnit[0].hp = self.targetUnit[0].hp - max(0, hits-defends)
        unitCaster[0].hp = min(unitCaster[0].maxhp, unitCaster[0].hp + max(0, hits-defends))
        locTarget = self.targetUnit
        self.targetUnit = None
        return ATTACK, locTarget, hits, defends
        
#Kirby's ability
class Swallow:
    name = "Swallow"
    description = "Take an enemy's ability as your own. Can only use once. Range:2"
    skillRange = 2
    def canUse(self, gameState, unitCaster, targetPoint):
        unitFound = False
        unit = None
        for p in gameState["players"]:
            for u in p.units:
                if u[1].collidepoint(targetPoint) and not unitCaster in p.units:
                    self.targetUnit = u
                    unitFound = True
                    break
                
        if not unitFound:
            return False

        x1 = unitCaster[1].left // 50
        y1 = unitCaster[1].top // 50
        x2 = self.targetUnit[1].left // 50 
        y2 = self.targetUnit[1].top // 50
        
        distance = Utility.manhattan((x1,y1), (x2,y2))
        
        if distance > self.skillRange:
            return False
        
        sight = Utility.lineOfSight((unitCaster[1].centerx, unitCaster[1].centery),
                                    (self.targetUnit[1].centerx, self.targetUnit[1].centery),
                                    gameState)
        
        if not sight:
            return False
        
        return self.targetUnit[0].skill != None

    def use (self, gameState, unitCaster, targetPoint):
        unitCaster[0].skill = copy.copy(self.targetUnit[0].skill)
        self.targetUnit = None
        return [PASSIVE]        